/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.rop.code;

import com.android.dx.util.Hex;

/**
 * All the register-based opcodes, and related utilities.
 * <p>
 * <b>Note:</b> Opcode descriptions use a rough pseudocode. {@code r} is the
 * result register, {@code x} is the first argument, {@code y} is the second
 * argument, and {@code z} is the third argument. The expression which describes
 * the operation uses Java-ish syntax but is preceded by type indicators for
 * each of the values.
 */
public final class RegOps {

	/** {@code nop()} */
	public static final int NOP = 1;

	/** {@code T: any type; r,x: T :: r = x;} */
	public static final int MOVE = 2;

	/** {@code T: any type; r,param(x): T :: r = param(x)} */
	public static final int MOVE_PARAM = 3;

	/**
	 * {@code T: Throwable; r: T :: r = caught_exception}. <b>Note:</b> This
	 * opcode should only ever be used in the first instruction of a block, and
	 * such blocks must be the start of an exception handler.
	 */
	public static final int MOVE_EXCEPTION = 4;

	/** {@code T: any type; r, literal: T :: r = literal;} */
	public static final int CONST = 5;

	/** {@code goto label} */
	public static final int GOTO = 6;

	/**
	 * {@code T: int or Object; x,y: T :: if (x == y) goto
	 * label}
	 */
	public static final int IF_EQ = 7;

	/**
	 * {@code T: int or Object; x,y: T :: if (x != y) goto
	 * label}
	 */
	public static final int IF_NE = 8;

	/** {@code x,y: int :: if (x < y) goto label} */
	public static final int IF_LT = 9;

	/** {@code x,y: int :: if (x >= y) goto label} */
	public static final int IF_GE = 10;

	/** {@code x,y: int :: if (x <= y) goto label} */
	public static final int IF_LE = 11;

	/** {@code x,y: int :: if (x > y) goto label} */
	public static final int IF_GT = 12;

	/** {@code x: int :: goto table[x]} */
	public static final int SWITCH = 13;

	/** {@code T: any numeric type; r,x,y: T :: r = x + y} */
	public static final int ADD = 14;

	/** {@code T: any numeric type; r,x,y: T :: r = x - y} */
	public static final int SUB = 15;

	/** {@code T: any numeric type; r,x,y: T :: r = x * y} */
	public static final int MUL = 16;

	/** {@code T: any numeric type; r,x,y: T :: r = x / y} */
	public static final int DIV = 17;

	/**
	 * {@code T: any numeric type; r,x,y: T :: r = x % y} (Java-style remainder)
	 */
	public static final int REM = 18;

	/** {@code T: any numeric type; r,x: T :: r = -x} */
	public static final int NEG = 19;

	/** {@code T: any integral type; r,x,y: T :: r = x & y} */
	public static final int AND = 20;

	/** {@code T: any integral type; r,x,y: T :: r = x | y} */
	public static final int OR = 21;

	/** {@code T: any integral type; r,x,y: T :: r = x ^ y} */
	public static final int XOR = 22;

	/**
	 * {@code T: any integral type; r,x: T; y: int :: r = x << y}
	 */
	public static final int SHL = 23;

	/**
	 * {@code T: any integral type; r,x: T; y: int :: r = x >> y} (signed
	 * right-shift)
	 */
	public static final int SHR = 24;

	/**
	 * {@code T: any integral type; r,x: T; y: int :: r = x >>> y} (unsigned
	 * right-shift)
	 */
	public static final int USHR = 25;

	/** {@code T: any integral type; r,x: T :: r = ~x} */
	public static final int NOT = 26;

	/**
	 * {@code T: any numeric type; r: int; x,y: T :: r = (x == y) ? 0
	 * : (x > y) ? 1 : -1} (Java-style "cmpl" where a NaN is considered
	 * "less than" all other values; also used for integral comparisons)
	 */
	public static final int CMPL = 27;

	/**
	 * {@code T: any floating point type; r: int; x,y: T :: r = (x == y) ? 0
	 * : (x < y) ? -1 : 1} (Java-style "cmpg" where a NaN is considered
	 * "greater than" all other values)
	 */
	public static final int CMPG = 28;

	/**
	 * {@code T: any numeric type; U: any numeric type; r: T; x: U ::
	 * r = (T) x} (numeric type conversion between the four "real" numeric
	 * types)
	 */
	public static final int CONV = 29;

	/**
	 * {@code r,x: int :: r = (x << 24) >> 24} (Java-style convert int to byte)
	 */
	public static final int TO_BYTE = 30;

	/**
	 * {@code r,x: int :: r = x & 0xffff} (Java-style convert int to char)
	 */
	public static final int TO_CHAR = 31;

	/**
	 * {@code r,x: int :: r = (x << 16) >> 16} (Java-style convert int to short)
	 */
	public static final int TO_SHORT = 32;

	/** {@code T: return type for the method; x: T; return x} */
	public static final int RETURN = 33;

	/** {@code T: any type; r: int; x: T[]; :: r = x.length} */
	public static final int ARRAY_LENGTH = 34;

	/** {@code x: Throwable :: throw(x)} */
	public static final int THROW = 35;

	/** {@code x: Object :: monitorenter(x)} */
	public static final int MONITOR_ENTER = 36;

	/** {@code x: Object :: monitorexit(x)} */
	public static final int MONITOR_EXIT = 37;

	/** {@code T: any type; r: T; x: T[]; y: int :: r = x[y]} */
	public static final int AGET = 38;

	/** {@code T: any type; x: T; y: T[]; z: int :: x[y] = z} */
	public static final int APUT = 39;

	/**
	 * {@code T: any non-array object type :: r =
	 * alloc(T)} (allocate heap space for an object)
	 */
	public static final int NEW_INSTANCE = 40;

	/** {@code T: any array type; r: T; x: int :: r = new T[x]} */
	public static final int NEW_ARRAY = 41;

	/**
	 * {@code T: any array type; r: T; x: int; v0..vx: T :: r = new T[x] v0,
	 * ..., vx}
	 */
	public static final int FILLED_NEW_ARRAY = 42;

	/**
	 * {@code T: any object type; x: Object :: (T) x} (can throw
	 * {@code ClassCastException})
	 */
	public static final int CHECK_CAST = 43;

	/**
	 * {@code T: any object type; x: Object :: x instanceof T}
	 */
	public static final int INSTANCE_OF = 44;

	/**
	 * {@code T: any type; r: T; x: Object; f: instance field spec of
	 * type T :: r = x.f}
	 */
	public static final int GET_FIELD = 45;

	/**
	 * {@code T: any type; r: T; f: static field spec of type T :: r =
	 * f}
	 */
	public static final int GET_STATIC = 46;

	/**
	 * {@code T: any type; x: T; y: Object; f: instance field spec of type
	 * T :: y.f = x}
	 */
	public static final int PUT_FIELD = 47;

	/**
	 * {@code T: any type; f: static field spec of type T; x: T :: f = x}
	 */
	public static final int PUT_STATIC = 48;

	/**
	 * {@code Tr, T0, T1...: any types; r: Tr; m: static method spec;
	 * y0: T0; y1: T1 ... :: r = m(y0, y1, ...)} (call static method)
	 */
	public static final int INVOKE_STATIC = 49;

	/**
	 * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
	 * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call normal virtual
	 * method)
	 */
	public static final int INVOKE_VIRTUAL = 50;

	/**
	 * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
	 * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call superclass
	 * virtual method)
	 */
	public static final int INVOKE_SUPER = 51;

	/**
	 * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
	 * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call direct/special
	 * method)
	 */
	public static final int INVOKE_DIRECT = 52;

	/**
	 * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: interface
	 * (instance) method spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1,
	 * ...)} (call interface method)
	 */
	public static final int INVOKE_INTERFACE = 53;

	/**
	 * {@code T0: any type; name: local variable name  :: mark(name,T0)} (mark
	 * beginning or end of local variable name)
	 */
	public static final int MARK_LOCAL = 54;

	/**
	 * {@code T: Any type; r: T :: r = return_type}. <b>Note:</b> This opcode
	 * should only ever be used in the first instruction of a block following an
	 * invoke-*.
	 */
	public static final int MOVE_RESULT = 55;

	/**
	 * {@code T: Any type; r: T :: r = return_type}. <b>Note:</b> This opcode
	 * should only ever be used in the first instruction of a block following a
	 * non-invoke throwing insn
	 */
	public static final int MOVE_RESULT_PSEUDO = 56;

	/** {@code T: Any primitive type; v0..vx: T :: v0, ..., vx} */
	public static final int FILL_ARRAY_DATA = 57;

	/**
	 * This class is uninstantiable.
	 */
	private RegOps() {
		// This space intentionally left blank.
	}

	/**
	 * Gets the name of the given opcode.
	 * 
	 * @param opcode
	 *            the opcode
	 * @return {@code non-null;} its name
	 */
	public static String opName(int opcode) {
		switch (opcode) {
		case NOP:
			return "nop";
		case MOVE:
			return "move";
		case MOVE_PARAM:
			return "move-param";
		case MOVE_EXCEPTION:
			return "move-exception";
		case CONST:
			return "const";
		case GOTO:
			return "goto";
		case IF_EQ:
			return "if-eq";
		case IF_NE:
			return "if-ne";
		case IF_LT:
			return "if-lt";
		case IF_GE:
			return "if-ge";
		case IF_LE:
			return "if-le";
		case IF_GT:
			return "if-gt";
		case SWITCH:
			return "switch";
		case ADD:
			return "add";
		case SUB:
			return "sub";
		case MUL:
			return "mul";
		case DIV:
			return "div";
		case REM:
			return "rem";
		case NEG:
			return "neg";
		case AND:
			return "and";
		case OR:
			return "or";
		case XOR:
			return "xor";
		case SHL:
			return "shl";
		case SHR:
			return "shr";
		case USHR:
			return "ushr";
		case NOT:
			return "not";
		case CMPL:
			return "cmpl";
		case CMPG:
			return "cmpg";
		case CONV:
			return "conv";
		case TO_BYTE:
			return "to-byte";
		case TO_CHAR:
			return "to-char";
		case TO_SHORT:
			return "to-short";
		case RETURN:
			return "return";
		case ARRAY_LENGTH:
			return "array-length";
		case THROW:
			return "throw";
		case MONITOR_ENTER:
			return "monitor-enter";
		case MONITOR_EXIT:
			return "monitor-exit";
		case AGET:
			return "aget";
		case APUT:
			return "aput";
		case NEW_INSTANCE:
			return "new-instance";
		case NEW_ARRAY:
			return "new-array";
		case FILLED_NEW_ARRAY:
			return "filled-new-array";
		case CHECK_CAST:
			return "check-cast";
		case INSTANCE_OF:
			return "instance-of";
		case GET_FIELD:
			return "get-field";
		case GET_STATIC:
			return "get-static";
		case PUT_FIELD:
			return "put-field";
		case PUT_STATIC:
			return "put-static";
		case INVOKE_STATIC:
			return "invoke-static";
		case INVOKE_VIRTUAL:
			return "invoke-virtual";
		case INVOKE_SUPER:
			return "invoke-super";
		case INVOKE_DIRECT:
			return "invoke-direct";
		case INVOKE_INTERFACE:
			return "invoke-interface";
		case MOVE_RESULT:
			return "move-result";
		case MOVE_RESULT_PSEUDO:
			return "move-result-pseudo";
		case FILL_ARRAY_DATA:
			return "fill-array-data";
		}

		return "unknown-" + Hex.u1(opcode);
	}

	/**
	 * Given an IF_* RegOp, returns the right-to-left flipped version. For
	 * example, IF_GT becomes IF_LT.
	 * 
	 * @param opcode
	 *            An IF_* RegOp
	 * @return flipped IF Regop
	 */
	public static int flippedIfOpcode(final int opcode) {
		switch (opcode) {
		case RegOps.IF_EQ:
		case RegOps.IF_NE:
			return opcode;
		case RegOps.IF_LT:
			return RegOps.IF_GT;
		case RegOps.IF_GE:
			return RegOps.IF_LE;
		case RegOps.IF_LE:
			return RegOps.IF_GE;
		case RegOps.IF_GT:
			return RegOps.IF_LT;
		default:
			throw new RuntimeException("Unrecognized IF regop: " + opcode);
		}
	}
}
